05 调试、安全与最佳实践
MCP Server连接的是模型和外部系统。它既要能被模型稳定调用,也要控制好权限、输入、日志和副作用。开发完成最小Server后,应尽早建立调试流程和安全基线。
一、调试工具
MCP官方提供了MCP Inspector,用于测试和调试MCP Server。Inspector可以连接stdio或Streamable HTTP Server,并查看Server暴露的Tools、Resources、Prompts和通知。
常用启动方式:
npx @modelcontextprotocol/inspector <command>调试本地Python Server:
npx @modelcontextprotocol/inspector \
uv \
--directory /ABSOLUTE/PATH/docs-mcp-server \
run \
server.py调试npm包形式的Server:
npx -y @modelcontextprotocol/inspector \
npx \
@modelcontextprotocol/server-filesystem \
/Users/username/DesktopInspector适合检查以下内容:
- Server是否能正常初始化。
- 能力协商是否符合预期。
- Tools是否能被发现。
- Tool参数schema是否正确。
- Resources是否能读取。
- Prompts是否能获取。
- Server是否发送了错误或通知。
二、推荐调试流程
开发MCP Server时,可以采用以下流程:
- 独立运行Server命令:确认依赖、路径、环境变量没有问题。
- 使用Inspector连接Server:检查初始化和能力发现。
- 测试Tools:覆盖正常参数、缺失参数、非法参数和空结果。
- 测试Resources:检查URI、MIME类型、内容格式和不存在资源的错误处理。
- 测试Prompts:检查参数定义和生成内容是否稳定。
- 接入真实Host:验证Host配置、权限提示和模型调用效果。
- 检查日志:确认错误信息可定位,且不泄露敏感数据。
不要只在Agent里直接调试MCP Server。模型行为会引入额外变量,容易掩盖Server本身的问题。应先用Inspector验证协议层和工具层。
三、stdio日志规范
stdio传输使用标准输入输出传递JSON-RPC消息。因此,Server不能向stdout输出普通日志。
错误写法:
print("tool called")推荐写法:
import sys
print("tool called", file=sys.stderr)或使用日志库:
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info("tool called")日志建议包含:
- 初始化信息
- 工具调用名称
- 参数摘要
- 权限判断结果
- 外部系统调用耗时
- 错误类型和错误上下文
日志不应包含:
- 明文Token
- 用户密码
- 完整密钥
- 敏感业务数据
- 大段模型上下文
四、工具安全边界
Tools是模型可调用的动作入口,需要比Resources和Prompts更严格的安全控制。
工具可以按风险分级:
| 风险等级 | 示例 | 建议控制 |
|---|---|---|
| 低风险 | 查询天气、读取公开文档 | 参数校验、基础日志 |
| 中风险 | 查询内部数据、搜索客户记录 | 用户鉴权、数据范围限制、审计 |
| 高风险 | 写数据库、删除文件、发送消息、创建工单 | 人工确认、权限校验、幂等设计、完整审计 |
模型能看到工具,不代表工具一定可以无条件执行。Client或Server应根据用户身份、业务场景和风险等级决定是否允许调用。
五、参数校验
MCP工具会通过schema描述输入参数,但服务端仍然需要做业务校验。
示例:
@mcp.tool()
def get_order(order_id: str) -> dict:
"""Get order by order id."""
if not order_id.startswith("ORD-"):
raise ValueError("order_id must start with ORD-")
return query_order(order_id)需要校验的内容包括:
- 必填字段
- 字符串长度
- 枚举值
- 数字范围
- 日期范围
- 用户是否有权访问目标资源
- 操作是否允许重复执行
对外部系统的错误不要原样暴露给模型或用户。应转换成可理解、可处理、不会泄露内部细节的错误信息。
六、权限控制
MCP协议提供标准连接方式,但不会自动替业务系统完成权限控制。权限应在Host、Client或Server侧明确实现。
常见做法包括:
- 基于用户身份过滤可见工具。
- 对Tool调用做服务端鉴权。
- 对Resource按用户权限裁剪可读范围。
- 为高风险工具加入二次确认。
- 对所有写操作记录审计日志。
- 对远程Server使用Bearer Token、OAuth或企业身份系统。
在多租户场景中,Server必须保证租户隔离。不要只依赖模型提示词限制访问范围。
七、远程Server安全
Streamable HTTP Server暴露网络端点后,需要额外关注网络安全。
建议:
- 使用HTTPS。
- 实现认证和授权。
- 校验
Origin头,降低DNS rebinding风险。 - 本地开发服务绑定
127.0.0.1,避免暴露到局域网。 - 对请求大小、频率和超时时间做限制。
- 对SSE连接设置合理超时和资源限制。
- 对会话ID、Token等敏感字段做安全生成和存储。
如果Server只是本地开发使用,不应绑定到0.0.0.0。如果必须对外暴露,应先确认认证、授权和网络访问策略已经到位。
八、工具描述最佳实践
工具描述会影响模型的调用选择。描述过短或过泛,会导致模型误用工具。
推荐写法:
@mcp.tool()
def search_docs(query: str, limit: int = 5) -> list[dict]:
"""Search internal documentation.
Use this tool when the user asks about project documentation,
development standards, deployment steps, or troubleshooting guides.
Args:
query: Search keyword or natural language question.
limit: Maximum number of results, from 1 to 10.
"""
...设计建议:
- 工具名使用动词开头,例如
search_docs、create_ticket。 - 描述中写清楚适用场景。
- 参数说明包含格式和限制。
- 默认值要安全。
- 高风险工具要在描述中明确副作用。
不推荐的工具设计:
@mcp.tool()
def do_task(input: str) -> str:
"""Do a task."""
...这种工具名和描述都过于模糊,模型难以判断何时调用,也不利于审计。
九、错误处理
工具执行失败时,应返回明确的错误信息。
常见错误类型:
| 类型 | 示例 | 处理方式 |
|---|---|---|
| 参数错误 | 日期格式不正确 | 提示正确格式 |
| 权限错误 | 无权访问该订单 | 返回权限不足,不泄露资源细节 |
| 外部服务错误 | 数据库超时 | 返回暂时不可用,并记录日志 |
| 空结果 | 未找到文档 | 返回空结果说明 |
| 业务限制 | 订单已关闭,不能修改 | 返回业务原因 |
错误信息应帮助模型或用户修正请求,而不是只返回“失败”。
十、上线检查清单
MCP Server上线前可以检查以下内容:
| 检查项 | 说明 |
|---|---|
| 能力边界 | Tools、Resources、Prompts职责是否清晰 |
| 参数schema | 是否定义类型、必填字段、枚举和范围 |
| 权限控制 | 是否按用户身份限制工具和资源 |
| 副作用控制 | 写入、删除、发送类工具是否需要确认 |
| 日志 | 是否记录调用、错误和耗时 |
| 敏感信息 | 日志和错误中是否避免泄露Token、密钥和隐私数据 |
| 传输安全 | HTTP Server是否使用认证、HTTPS和Origin校验 |
| 调试验证 | 是否通过Inspector验证工具、资源、提示词 |
| 配置 | 是否使用绝对路径、正确环境变量和安全密钥管理 |
| 兼容性 | Client和Server协议版本是否兼容 |
十一、总结
MCP Server开发完成后,调试和安全控制决定它能否稳定进入真实Agent系统。
| 主题 | 重点 |
|---|---|
| 调试 | 使用Inspector先验证协议和能力,再接入Agent |
| 日志 | stdio模式不要写stdout,敏感信息不要进日志 |
| 参数 | schema之外仍要做业务校验 |
| 权限 | 不依赖模型自觉,Server侧必须校验 |
| 副作用 | 高风险工具需要确认、审计和幂等设计 |
| 远程服务 | 使用认证、HTTPS、Origin校验和访问限制 |
MCP提供的是标准协议,不是完整安全方案。生产环境中的权限、审计、确认和数据隔离仍需要在应用和Server实现中明确设计。